Skip to main content

七牛云文件上传

七牛云文件上传

pom.xml

<!--web lombok qiniu-->
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>[7.19.0, 7.19.99]</version>
</dependency>

application.yml

server:
port: 8101

qiniu:
accessKey: ak
secretKey: sk
bucket: bu
domainOfBucket: dob

config

    // =============== 配置类 ===============
@Data
@Component
@ConfigurationProperties(prefix = "qiniu")
public static class QiNiuConfig {
private String accessKey;
private String secretKey;
private String bucket;
private String domainOfBucket;
}

service

    // =============== 七牛服务组件 ===============
@Slf4j
@Component
public static class QiNiuService {
private final UploadManager uploadManager;
private final BucketManager bucketManager;
private final Auth auth;
private final QiNiuConfig config;

public QiNiuService(QiNiuConfig config) {
this.config = config;
this.auth = Auth.create(config.getAccessKey(), config.getSecretKey());

Configuration cfg = Configuration.create(Region.autoRegion());
cfg.resumableUploadAPIVersion = Configuration.ResumableUploadAPIVersion.V2;
this.uploadManager = new UploadManager(cfg);
bucketManager = new BucketManager(auth, cfg);
}

public String upload(MultipartFile file) throws IOException {
if (file == null || file.isEmpty()) {
throw new IllegalArgumentException("上传文件为空");
}
return uploadStream(file.getInputStream(), file.getOriginalFilename());
}

public String uploadBytes(byte[] bytes, String filename){
if (bytes == null || bytes.length == 0) {
throw new IllegalArgumentException("文件内容为空");
}

String upToken = uploadToken();
String key = generateUniqueKey(filename);

try {
Response response = uploadManager.put(bytes, key, upToken);
DefaultPutRet putRet = response.jsonToObject(DefaultPutRet.class);
log.info("文件上传成功: key={}, hash={}", putRet.key, putRet.hash);
return config.getDomainOfBucket() + "/" + putRet.key;
} catch (QiniuException ex) {
log.error("七牛上传失败 (bytes)", ex);
handleQiniuException(ex);
throw new RuntimeException("上传到七牛失败", ex);
}
}

public void delete(String fileName) {
try {
bucketManager.delete(config.getBucket(), fileName);
log.info("文件删除成功: key={}", fileName);
} catch (QiniuException ex) {
log.error("七牛删除失败: key={}", fileName, ex);
handleQiniuException(ex);
throw new RuntimeException("删除七牛文件失败", ex);
}
}

public void deleteBatch(String... fileNameList) {
if (fileNameList == null || fileNameList.length == 0) {
return;
}
if (fileNameList.length > 1000) {
throw new IllegalArgumentException("批量删除数量不能超过1000");
}

try {
BucketManager.BatchOperations batchOperations = new BucketManager.BatchOperations();
batchOperations.addDeleteOp(config.getBucket(), fileNameList);
Response response = bucketManager.batch(batchOperations);
BatchStatus[] batchStatusList = response.jsonToObject(BatchStatus[].class);

for (int i = 0; i < fileNameList.length; i++) {
BatchStatus status = batchStatusList[i];
String key = fileNameList[i];
if (status.code == 200) {
log.info("批量删除成功: key={}", key);
} else {
log.warn("批量删除失败: key={}, error={}", key, status.data.error);
}
}
} catch (QiniuException ex) {
log.error("七牛批量删除异常", ex);
handleQiniuException(ex);
throw new RuntimeException("批量删除失败", ex);
}
}

public String uploadStream(InputStream stream, String filename){
if (stream == null) {
throw new IllegalArgumentException("输入流为空");
}
// 注意:stream.available() 不可靠,不用于判断是否为空

String upToken = uploadToken();
String key = generateUniqueKey(filename);

try {
Response response = uploadManager.put(stream, key, upToken, null, null);
DefaultPutRet putRet = response.jsonToObject(DefaultPutRet.class);
log.info("流式上传成功: key={}, hash={}", putRet.key, putRet.hash);
return config.getDomainOfBucket() + "/" + putRet.key;
} catch (QiniuException ex) {
log.error("七牛流式上传失败", ex);
handleQiniuException(ex);
throw new RuntimeException("上传到七牛失败", ex);
}
}

public String uploadToken() {
return auth.uploadToken(config.getBucket());
}
// === 工具方法 ===
private String generateUniqueKey(String originalFilename) {
String ext = "";
if (originalFilename != null && originalFilename.contains(".")) {
ext = originalFilename.substring(originalFilename.lastIndexOf("."));
}
return UUID.randomUUID() + ext;
}

private void handleQiniuException(QiniuException ex) {
Response r = ex.response;
if (r != null) {
try {
log.error("七牛错误响应: code={}, body={}", r.statusCode, r.bodyString());
} catch (Exception e) {
log.error("读取七牛错误响应体失败", e);
}
}
}
}

controller


// =============== Controller ===============
@RestController
@RequiredArgsConstructor
public static class UploadController {

private final QiNiuService qiNiuService;

@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
return qiNiuService.upload(file);
}

@PostMapping("/upload-multi")
public List<String> uploadFiles(@RequestParam("files") List<MultipartFile> files) {
return files.stream()
.map(file -> {
try {
return qiNiuService.upload(file);
} catch (IOException e) {
log.error("多文件上传失败: filename={}", file.getOriginalFilename(), e);
throw new RuntimeException("文件上传失败: " + file.getOriginalFilename(), e);
}
})
.collect(Collectors.toList());
}

@PostMapping(value = "/upload-stream", consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public String uploadStream(HttpServletRequest request) throws IOException {
try (InputStream is = request.getInputStream()) {
byte[] bytes = is.readAllBytes();
String filename = "stream_" + System.currentTimeMillis() + ".bin";
return qiNiuService.uploadBytes(bytes, filename);
}
}

@PostMapping("/upload-with-meta")
public String uploadWithMeta(
@RequestParam("file") MultipartFile file,
@RequestParam("userId") String userId,
@RequestParam("category") String category) {
log.info("带元数据上传: userId={}, category={}, filename={}", userId, category, file.getOriginalFilename());
try {
return qiNiuService.upload(file);
} catch (IOException e) {
log.error("带元数据上传失败", e);
throw new RuntimeException(e);
}
}


/**
* 大文件上传,给前端token
*/
@GetMapping("/uptoken")
public String getUpToken() {
return qiNiuService.uploadToken();
}
@GetMapping("/delete")
public void delete(@RequestParam("fileName") String fileName) {
qiNiuService.delete(fileName);
}
@GetMapping("/delete-batch")
public void deleteBatch(@RequestParam("key") String... fileNameList) {
qiNiuService.deleteBatch(fileNameList);
}
}

完整代码

@Slf4j
@SpringBootApplication
public class QiniuApplication {

public static void main(String[] args) {
SpringApplication.run(QiniuApplication.class, args);
}

// =============== 配置类 ===============
@Data
@Component
@ConfigurationProperties(prefix = "qiniu")
public static class QiNiuConfig {
private String accessKey;
private String secretKey;
private String bucket;
private String domainOfBucket;
}

// =============== 七牛服务组件 ===============
@Slf4j
@Component
public static class QiNiuService {
private final UploadManager uploadManager;
private final BucketManager bucketManager;
private final Auth auth;
private final QiNiuConfig config;

public QiNiuService(QiNiuConfig config) {
this.config = config;
this.auth = Auth.create(config.getAccessKey(), config.getSecretKey());

Configuration cfg = Configuration.create(Region.autoRegion());
cfg.resumableUploadAPIVersion = Configuration.ResumableUploadAPIVersion.V2;
this.uploadManager = new UploadManager(cfg);
bucketManager = new BucketManager(auth, cfg);
}

public String upload(MultipartFile file) throws IOException {
if (file == null || file.isEmpty()) {
throw new IllegalArgumentException("上传文件为空");
}
return uploadStream(file.getInputStream(), file.getOriginalFilename());
}

public String uploadBytes(byte[] bytes, String filename){
if (bytes == null || bytes.length == 0) {
throw new IllegalArgumentException("文件内容为空");
}

String upToken = uploadToken();
String key = generateUniqueKey(filename);

try {
Response response = uploadManager.put(bytes, key, upToken);
DefaultPutRet putRet = response.jsonToObject(DefaultPutRet.class);
log.info("文件上传成功: key={}, hash={}", putRet.key, putRet.hash);
return config.getDomainOfBucket() + "/" + putRet.key;
} catch (QiniuException ex) {
log.error("七牛上传失败 (bytes)", ex);
handleQiniuException(ex);
throw new RuntimeException("上传到七牛失败", ex);
}
}

public void delete(String fileName) {
try {
bucketManager.delete(config.getBucket(), fileName);
log.info("文件删除成功: key={}", fileName);
} catch (QiniuException ex) {
log.error("七牛删除失败: key={}", fileName, ex);
handleQiniuException(ex);
throw new RuntimeException("删除七牛文件失败", ex);
}
}

public void deleteBatch(String... fileNameList) {
if (fileNameList == null || fileNameList.length == 0) {
return;
}
if (fileNameList.length > 1000) {
throw new IllegalArgumentException("批量删除数量不能超过1000");
}

try {
BucketManager.BatchOperations batchOperations = new BucketManager.BatchOperations();
batchOperations.addDeleteOp(config.getBucket(), fileNameList);
Response response = bucketManager.batch(batchOperations);
BatchStatus[] batchStatusList = response.jsonToObject(BatchStatus[].class);

for (int i = 0; i < fileNameList.length; i++) {
BatchStatus status = batchStatusList[i];
String key = fileNameList[i];
if (status.code == 200) {
log.info("批量删除成功: key={}", key);
} else {
log.warn("批量删除失败: key={}, error={}", key, status.data.error);
}
}
} catch (QiniuException ex) {
log.error("七牛批量删除异常", ex);
handleQiniuException(ex);
throw new RuntimeException("批量删除失败", ex);
}
}

public String uploadStream(InputStream stream, String filename){
if (stream == null) {
throw new IllegalArgumentException("输入流为空");
}
// 注意:stream.available() 不可靠,不用于判断是否为空

String upToken = uploadToken();
String key = generateUniqueKey(filename);

try {
Response response = uploadManager.put(stream, key, upToken, null, null);
DefaultPutRet putRet = response.jsonToObject(DefaultPutRet.class);
log.info("流式上传成功: key={}, hash={}", putRet.key, putRet.hash);
return config.getDomainOfBucket() + "/" + putRet.key;
} catch (QiniuException ex) {
log.error("七牛流式上传失败", ex);
handleQiniuException(ex);
throw new RuntimeException("上传到七牛失败", ex);
}
}

public String uploadToken() {
return auth.uploadToken(config.getBucket());
}

// === 工具方法 ===
private String generateUniqueKey(String originalFilename) {
String ext = "";
if (originalFilename != null && originalFilename.contains(".")) {
ext = originalFilename.substring(originalFilename.lastIndexOf("."));
}
return UUID.randomUUID() + ext;
}

private void handleQiniuException(QiniuException ex) {
Response r = ex.response;
if (r != null) {
try {
log.error("七牛错误响应: code={}, body={}", r.statusCode, r.bodyString());
} catch (Exception e) {
log.error("读取七牛错误响应体失败", e);
}
}
}
}

// =============== Controller ===============
@RestController
@RequiredArgsConstructor
public static class UploadController {

private final QiNiuService qiNiuService;

@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
return qiNiuService.upload(file);
}

@PostMapping("/upload-multi")
public List<String> uploadFiles(@RequestParam("files") List<MultipartFile> files) {
return files.stream()
.map(file -> {
try {
return qiNiuService.upload(file);
} catch (IOException e) {
log.error("多文件上传失败: filename={}", file.getOriginalFilename(), e);
throw new RuntimeException("文件上传失败: " + file.getOriginalFilename(), e);
}
})
.collect(Collectors.toList());
}

@PostMapping(value = "/upload-stream", consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public String uploadStream(HttpServletRequest request) throws IOException {
try (InputStream is = request.getInputStream()) {
byte[] bytes = is.readAllBytes();
String filename = "stream_" + System.currentTimeMillis() + ".bin";
return qiNiuService.uploadBytes(bytes, filename);
}
}

@PostMapping("/upload-with-meta")
public String uploadWithMeta(
@RequestParam("file") MultipartFile file,
@RequestParam("userId") String userId,
@RequestParam("category") String category) {
log.info("带元数据上传: userId={}, category={}, filename={}", userId, category, file.getOriginalFilename());
try {
return qiNiuService.upload(file);
} catch (IOException e) {
log.error("带元数据上传失败", e);
throw new RuntimeException(e);
}
}


/**
* 大文件上传,给前端token
*/
@GetMapping("/uptoken")
public String getUpToken() {
return qiNiuService.uploadToken();
}
@GetMapping("/delete")
public void delete(@RequestParam("fileName") String fileName) {
qiNiuService.delete(fileName);
}
@GetMapping("/delete-batch")
public void deleteBatch(@RequestParam("key") String... fileNameList) {
qiNiuService.deleteBatch(fileNameList);
}
}
}